www.gusucode.com > VC++ 编写软件自动升级服务源代码 > VC++ 编写软件自动升级服务源代码/gusucode/updater_src0.8.1.6/Updater.cpp

    /********************************************************************
	created:	2005/03/02
	created:	2:3:2005   9:10
	filename: 	Updater.cpp
	file path:	Updater
	file base:	Updater
	file ext:	cpp
	author:		Geert van Horrik
	
	purpose:	
*********************************************************************/

//*********************************************************************
// INCLUDES
//*********************************************************************

#include "stdafx.h"
#include "UpdateProcess.h"
#include "CheckForUpdatesProcess.h"
#include "Updater.h"

#include "FileVersion.h"
#include "RestoreDlg.h"
#include "ProxySettingsDlg.h"
#include "SelectLanguageDlg.h"
#include "LogViewerDlg.h"

#include "ErrorDlg.h"
#include "WarningDlg.h"

//*********************************************************************
// MESSAGE MAP
//*********************************************************************

BEGIN_MESSAGE_MAP(CUpdaterApp, CWinApp)
	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

//*********************************************************************
// CONSTRUCTOR & DESTRUCTOR
//*********************************************************************

CUpdaterApp::CUpdaterApp()
{
}

//=====================================================================

CUpdaterApp theApp;

//*********************************************************************
// PUBLIC FUNCTIONS
//*********************************************************************

BOOL CUpdaterApp::InitInstance()
{
	// Set default return value
	m_iReturnValue = RETURN_OK;

	// Try
	try
	{
		// Catch any errors
		return Main();
	}
	catch (exception & ex)
	{
		// Error
		m_iReturnValue = RETURN_ERROR;

#ifdef _DEBUG
		ex.what();
#endif
	}
	catch (CException * pEx)
	{
		// Error
		m_iReturnValue = RETURN_ERROR;

#ifdef _DEBUG
		pEx->ReportError();
#endif
		pEx->Delete();
	}

	// If we get here, return 0
	return FALSE;
}

//=====================================================================

int CUpdaterApp::ExitInstance()
{
	// Call original function
	CWinApp::ExitInstance();

	// Return result code
	return m_iReturnValue;
}

//*********************************************************************
// PRIVATE FUNCTIONS
//*********************************************************************

BOOL CUpdaterApp::Main()
{
	// Initialise common control library (xp look)
	InitCommonControls();
	AfxEnableControlContainer();

	// Declare variables
	CString sTemp, sCommandLine, sSettingsFile;
	bool bLanguageChosen = false;
	CSettings * pSettings = CSettings::Instance();

	// Updater is not allowed to run when no user is logged in
	if (!IsUserLoggedIn())
	{
		// Quit updater
		return FALSE;
	}

	// Check what kind of release we are dealing with
#ifdef _BETA
	// We are using beta, show it to the user
	CWarningDlg dlgWarning(_T("Warning"), _T("Updater beta"), 
		_T("You are using a beta version of Updater. This version is for testing purposes only!\n\nUse an official release if you want to use Updater in your software."));
	dlgWarning.DoModal();
#endif

	// Initialize all paths
	CPath * pPath = CPath::Instance();
	if (!pPath->Init())
		return FALSE;

	// Parse command line to get settings file
	try
	{
		sSettingsFile = pSettings->ParseCommandLineForSettingsFile(m_lpCmdLine);
	}	
	catch (exception & ex)
	{
		// Declare variables
		CString sTitle, sExplanation, sMore;
		CLanguage * pLanguage = CLanguage::Instance();

		// Set up error message
		sTitle = _T("Parameter parsing error");
		sExplanation.Format(_T("The following exception occurred when parsing parameters:\n\n\"%s\".\nCommandline: %s"), ex.what(), m_lpCmdLine);
		sMore = _T("Check if the parameters being used are correct.\n\nIf the error keeps occurring, contact the developer at ") +
			UPDATER_WEBADDRESS + _T(".");

		// Show error dialog
		CErrorDlg dlgError(sTitle, pLanguage->GetString(IDS_ERROR_TITLE), sExplanation,
			sMore, pLanguage->GetString(IDS_GENERAL_OK), pLanguage->GetString(IDS_ERROR_MORE));
		dlgError.DoModal();

		// Quit Updater
		return FALSE;
	}

	// Read settings.ini
	if (!PathFileExists(sSettingsFile))
	{
		CInternalData::Instance()->SetSettingsFile(pPath->GetPathUpdater() + FILENAME_SETTINGS);
	}
	else
	{
		CInternalData::Instance()->SetSettingsFile(sSettingsFile);
	}
	pSettings->ReadFile(CInternalData::Instance()->GetSettingsFile());

	// Always delete UpdaterCopy.exe from temp dir
	DeleteFile(pPath->GetPathTemp() + _T("UpdaterCopy.exe"));

	// Parse command line, called after the settings, because this
	// should overrule the settings from the ini file
	try
	{
		pSettings->ParseCommandLine(m_lpCmdLine);
	}
	catch (exception & ex)
	{
		// Declare variables
		CString sTitle, sExplanation, sMore;
		CLanguage * pLanguage = CLanguage::Instance();

		// Set up error message
		sTitle = _T("Parameter parsing error");
		sExplanation.Format(_T("The following exception occurred when parsing parameters:\n\n\"%s\".\nCommandline: %s"), ex.what(), m_lpCmdLine);
		sMore = _T("Check if the parameters being used are correct.\n\nIf the error keeps occurring, contact the developer at ") +
			UPDATER_WEBADDRESS + _T(".");

		// Show error dialog
		CErrorDlg dlgError(sTitle, pLanguage->GetString(IDS_ERROR_TITLE), sExplanation,
			sMore, pLanguage->GetString(IDS_GENERAL_OK), pLanguage->GetString(IDS_ERROR_MORE));
		dlgError.DoModal();

		// Quit Updater
		return FALSE;
	}

	// Create mutex, because there cannot be 2 instances of Updater for same application
	HANDLE hMutex = CreateMutex(NULL, FALSE, _T("updater_") + pSettings->GetAppName()); 

	// Check if mutex is created succesfully
	switch(GetLastError())
	{
	case ERROR_SUCCESS:
		// Mutex created successfully. There is no instance running
		break;
		
	case ERROR_ALREADY_EXISTS:
		// Mutex already exists so there is a running instance of our app.
		return FALSE;
		
	default:
		// Failed to create mutex by unknown reason
		return FALSE;
	}

	// Create needed folders such as temp and rollback
	if (!pPath->CreateUpdaterFolders())
		return FALSE;

	// Load the right language - auto
	CLanguage * pLanguage = CLanguage::Instance();
	if ((pSettings->GetLanguage() == LANGUAGETYPE_AUTO) && !bLanguageChosen)
	{
		// We have chosen a language
		bLanguageChosen = true;

		// Automatically load
		pLanguage->LoadLanguage(GetSystemDefaultLangID());
	}

	// Load the right language - user
	if ((pSettings->GetLanguage() == LANGUAGETYPE_USER) && !bLanguageChosen)
	{
		// We have chosen a language
		bLanguageChosen = true;

		// Let user choose
		CSelectLanguageDlg dlgLanguage;
		dlgLanguage.DoModal();

		// Load chosen language
		sTemp.Format(_T("%s\\lng\\%s.xml"), pPath->GetPathUpdater(), pSettings->GetLanguage());
		pLanguage->LoadLanguage(sTemp, pSettings->GetLanguage());
	}

	// Load the right language - settings
	if (!bLanguageChosen)
	{
		// Load chosen language
		sTemp.Format(_T("%s\\lng\\%s.xml"), pPath->GetPathUpdater(), pSettings->GetLanguage());
		pLanguage->LoadLanguage(sTemp, pSettings->GetLanguage());
	}

	// Run right part of application
	sCommandLine.Format(_T("%s"), m_lpCmdLine);
	StartApplication(sCommandLine);

	// Release language
	pLanguage->ReleaseLanguage();

	// Exit Updater
	return TRUE;
}

//=====================================================================

void CUpdaterApp::StartApplication(CString sCommandLine)
{
	// Proxy settings
	if ((sCommandLine.Find(PARAMETER_PROXYSETTINGS) >= 0))
	{
		// Edit proxy settings
		StartProxySettings();
		return;		
	}

	// Restore
	if ((sCommandLine.Find(PARAMETER_RESTORE) >= 0))
	{
		// Start restoring
		StartRestore();
		return;
	}

	// Log viewer
	if ((sCommandLine.Find(PARAMETER_LOG) >= 0))
	{
		// Start log viewer
		StartViewLog();
		return;
	}

	// Check for updates
	if ((sCommandLine.Find(PARAMETER_CHECKFORUPDATES) >= 0))
	{
		// Start check for updates
		StartCheckForUpdates();
		return;
	}

	// Run Updater
	StartUpdater();
}

//=====================================================================

void CUpdaterApp::StartUpdater()
{
	// Declare variables
	CPath * pPath = CPath::Instance();
	CSettings * pSettings = CSettings::Instance();
	CString sTemp, sUpdaterVersion;;
	LARGE_INTEGER iFrequency, iTimeStart, iTimeEnd;

	// Calculate frequency
	QueryPerformanceFrequency(&iFrequency);

	// Record the time
	QueryPerformanceCounter(&iTimeStart);

	// Update to new version, create log file
	CFileVersion fileVersion;
	fileVersion.Open(pPath->GetPathUpdaterExecutable());
	sUpdaterVersion = fileVersion.GetFileVersion();
	CLog * pLog = CLog::Instance();
	pLog->CreateLog(pPath->GetPathTemp() + _T("Updater log for ") + pSettings->GetAppName() + _T(".txt"), false);
	pLog->Log(_T("UPDATER LOG FILE"));
	pLog->Log(_T("================"));
	pLog->Log(_T(""));
	pLog->Log(_T("Updater is developed by ") + UPDATER_DEVELOPER + _T(", ") + UPDATER_YEARS);
	pLog->Log(_T(""));
	pLog->Log(_T("Visit ") + UPDATER_WEBADDRESS + _T(" for more information."));
	pLog->Log(_T(""));
	pLog->Log(_T("*****************************************************"));
	fileVersion.Close();
	if (fileVersion.Open(pSettings->GetAppLocation()))
	{
		pLog->Log(_T(""));
		pLog->Log(_T("Application: ") + pSettings->GetAppName());
		pLog->Log(_T(""));
		pLog->Log(_T("Additional application data (begin)"));
		pLog->Log(_T("  Application data - product name:    ") + fileVersion.GetProductName());
		pLog->Log(_T("  Application data - current version: ") + fileVersion.GetFileVersion());
		pLog->Log(_T("  Application data - developer:       ") + fileVersion.GetCompanyName());
		pLog->Log(_T("  Application data - internal name:   ") + fileVersion.GetInternalName());
		pLog->Log(_T("  Application data - copyright:       ") + fileVersion.GetLegalCopyright());
		pLog->Log(_T("Additional application data (end)"));
		pLog->Log(_T(""));
		pLog->Log(_T("*****************************************************"));
		fileVersion.Close();
	}

	pLog->Log(_T(""));
	sTemp.Format(_T("Commandline: %s"), m_lpCmdLine);
	pLog->Log(sTemp);
	pLog->Log(_T(""));
	pLog->Log(_T("*****************************************************"));

	pLog->Log(_T(""));
	pLog->EnableTimestamp(true);

	// Start main update thread
	CUpdateProcess * pUpdateProcess;
	pUpdateProcess = (CUpdateProcess *)AfxBeginThread(RUNTIME_CLASS(CUpdateProcess),
													  THREAD_PRIORITY_NORMAL,
													  0,
													  0,
													  NULL);

	// Wait for thread to be ready
	WaitForSingleObject(pUpdateProcess->m_hThread, INFINITE);

	// Stop the time
	QueryPerformanceCounter(&iTimeEnd);

	// Save time to log file
	sTemp.Format(_T("Update took %s (hh:mm:ss:ms)"), ConvertTime((iTimeEnd.QuadPart - iTimeStart.QuadPart)
		/ (iFrequency.QuadPart / 1000)));
	pLog->EnableTimestamp(false);
	pLog->Log(_T(""));
	pLog->Log(_T("-------------------------------------"));
	pLog->Log(sTemp);
	pLog->Log(_T("-------------------------------------"));
	pLog->Log(_T(""));
	pLog->Log(_T("Close Updater"));

	// Update thread is ready
	pUpdateProcess = NULL;

	// Close log file
	pLog->CloseLog();
}

//=====================================================================

void CUpdaterApp::StartProxySettings()
{
	// Show proxy settings
	CProxySettingsDlg dlg;
	dlg.DoModal();
}

//=====================================================================

void CUpdaterApp::StartRestore()
{
	// Restore older version
	CRestoreDlg dlg;
	dlg.DoModal();
}

//=====================================================================

void CUpdaterApp::StartViewLog()
{
	// Show last log
	CLogViewerDlg dlg;
	dlg.DoModal();
}

//=====================================================================

void CUpdaterApp::StartCheckForUpdates()
{
	// Declare variables
	ULONG lResult = 0;

	// Start check for updates thread
	CCheckForUpdatesProcess * pCheckProcess;
	pCheckProcess = (CCheckForUpdatesProcess *)AfxBeginThread(RUNTIME_CLASS(CCheckForUpdatesProcess),
													 THREAD_PRIORITY_NORMAL,
													 0,
													 0,
													 NULL);

	// Don't auto delete the thread
	pCheckProcess->m_bAutoDelete = FALSE;

	// Wait until thread is complete
	WaitForSingleObject(pCheckProcess->m_hThread, INFINITE);

	// Get return code
	if (GetExitCodeThread(pCheckProcess->m_hThread, &lResult))
	{
		m_iReturnValue = lResult;
	}	
	else
	{
		// An error occurred
		m_iReturnValue = RETURN_ERROR;
	}

	// Delete thread object
	delete pCheckProcess;
}

//=====================================================================

bool CUpdaterApp::IsUserLoggedIn()
{
	// Declare variables
	bool bLoggedIn = false;

	// Get current desktop
	HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_READOBJECTS);

	// If we have a valid desktop
	if (hDesk)
	{
		// Get user object information
		SID sid;
		DWORD dwSidLength = 0;
		BOOL bSuccess = GetUserObjectInformation(hDesk, UOI_USER_SID, &sid, sizeof(SID), &dwSidLength);
		
		// Did we succeed or did we have a buffer error?
		if (!bSuccess && ERROR_INSUFFICIENT_BUFFER == GetLastError())
		{
			SID *psid = (SID *)malloc(dwSidLength);
			if (psid)
			{
				bSuccess = GetUserObjectInformation(hDesk, UOI_USER_SID, psid, dwSidLength, &dwSidLength);
				free(psid);
			}
		}

		// Did we succeed?
		if (bSuccess)
		{
			if (dwSidLength)
			{
				// A user has logged in
				bLoggedIn = true;
			}
			else
			{
				// No user has logged in
				bLoggedIn = false;
			}
		}

		// Close desktop
		CloseDesktop(hDesk);
	}

	// Return result
	return bLoggedIn;
}

//=====================================================================

CString CUpdaterApp::ConvertTime(int iTimeInMilliSeconds)
{
	// Declare variables
	int iHours, iMinutes, iSeconds;
	CString sResult;

	// Set default values
	iHours = 0;
	iMinutes = 0;
	iSeconds = 0;

	// Calculate hours
	while (iTimeInMilliSeconds > 3600000)
	{
		iHours++;
		iTimeInMilliSeconds -= 3600000;
	}

	// Calculate minutes
	while (iTimeInMilliSeconds > 60000)
	{
		iMinutes++;
		iTimeInMilliSeconds -= 60000;
	}

	// Calculate seconds
	while (iTimeInMilliSeconds > 1000)
	{
		iSeconds++;
		iTimeInMilliSeconds -= 1000;
	}

	// Setup string
	sResult.Format(_T("%d:%02d:%02d:%03d"), iHours, iMinutes, iSeconds, iTimeInMilliSeconds);

	// Return time
	return sResult;
}